/*
 * $Id: raffects.c 933 2006-11-19 22:37:00Z zsuzsu $
 */

/***************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR  *	
 *     ANATOLIA has been brought to you by ANATOLIA consortium		   *
 *	 Serdar BULUT {Chronos}		bulut@rorqual.cc.metu.edu.tr       *
 *	 Ibrahim Canpunar  {Asena}	canpunar@rorqual.cc.metu.edu.tr    *	
 *	 Murat BICER  {KIO}		mbicer@rorqual.cc.metu.edu.tr	   *	
 *	 D.Baris ACAR {Powerman}	dbacar@rorqual.cc.metu.edu.tr	   *	
 *     By using this code, you have agreed to follow the terms of the      *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence             *	
 ***************************************************************************/

/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
*	ROM 2.4 is copyright 1993-1995 Russ Taylor			   *
*	ROM has been brought to you by the ROM consortium		   *
*	    Russ Taylor (rtaylor@pacinfo.com)				   *
*	    Gabrielle Taylor (gtaylor@pacinfo.com)			   *
*	    Brian Moore (rom@rom.efn.org)				   *
*	By using this code, you have agreed to follow the terms of the	   *
*	ROM license, in the file Rom24/doc/rom.license			   *
***************************************************************************/

#include <sys/time.h>
#include <stdio.h>

#include "merc.h"
#include "raffects.h"
#include "fight.h"

DECLARE_DO_FUN(do_wake);
DECLARE_DO_FUN(do_visible);

/*
 * Apply or remove an affect to a room.
 */
void affect_modify_room(ROOM_INDEX_DATA * room, AFFECT_DATA * paf, bool fAdd)
{
	int mod;

	mod = paf->modifier;

	if (fAdd) {
		switch (paf->where) {
		case TO_ROOM_AFFECTS:
			SET_BIT(room->affected_by, paf->bitvector);
			break;
		case TO_ROOM_FLAGS:
			SET_BIT(room->room_flags, paf->bitvector);
			break;
		case TO_ROOM_CONST:
			break;
		}
	} else {
		switch (paf->where) {
		case TO_ROOM_AFFECTS:
			REMOVE_BIT(room->affected_by, paf->bitvector);
			break;
		case TO_ROOM_FLAGS:
			REMOVE_BIT(room->room_flags, paf->bitvector);
			break;
		case TO_ROOM_CONST:
			break;
		}
		mod = 0 - mod;
	}

	switch (paf->location) {
	default:
		bug("Affect_modify_room: unknown location %d.", paf->location);
		return;

	case APPLY_ROOM_NONE:
		break;
	case APPLY_ROOM_HEAL:
		room->heal_rate += mod;
		break;
	case APPLY_ROOM_MANA:
		room->mana_rate += mod;
		break;
	}

	return;
}

/*
 * Give an affect to a room.
 */
void affect_to_room(ROOM_INDEX_DATA * room, AFFECT_DATA * paf)
{
	AFFECT_DATA *paf_new;
	ROOM_INDEX_DATA *pRoomIndex;

	if (!room->affected) {
		if (top_affected_room) {
			for (pRoomIndex = top_affected_room;
			     pRoomIndex->aff_next != NULL;
			     pRoomIndex = pRoomIndex->aff_next)
				continue;
			pRoomIndex->aff_next = room;
		} else
			top_affected_room = room;
		room->aff_next = NULL;
	}

	paf_new = aff_new();

	*paf_new = *paf;
	paf_new->next = room->affected;
	room->affected = paf_new;

	affect_modify_room(room, paf_new, TRUE);
	return;
}

void affect_check_room(ROOM_INDEX_DATA * room, int where, int vector)
{
	AFFECT_DATA *paf;

	if (vector == 0)
		return;

	for (paf = room->affected; paf != NULL; paf = paf->next)
		if (paf->where == where && paf->bitvector == vector) {
			switch (where) {
			case TO_ROOM_AFFECTS:
				SET_BIT(room->affected_by, vector);
				break;
			case TO_ROOM_FLAGS:
				SET_BIT(room->room_flags, vector);
				break;
			case TO_ROOM_CONST:
				break;
			}
			return;
		}
}

/*
 * Remove an affect from a room.
 */
void affect_remove_room(ROOM_INDEX_DATA * room, AFFECT_DATA * paf)
{
	int where;
	int vector;


	if (room->affected == NULL) {
		bug("Affect_remove_room: no affect.", 0);
		return;
	}

	affect_modify_room(room, paf, FALSE);
	where = paf->where;
	vector = paf->bitvector;

	if (paf == room->affected) {
		room->affected = paf->next;
	} else {
		AFFECT_DATA *prev;

		for (prev = room->affected; prev != NULL; prev = prev->next) {
			if (prev->next == paf) {
				prev->next = paf->next;
				break;
			}
		}

		if (prev == NULL) {
			bug("Affect_remove_room: cannot find paf.", 0);
			return;
		}
	}

	if (!room->affected) {
		ROOM_INDEX_DATA *prev;

		if (top_affected_room == room) {
			top_affected_room = room->aff_next;
		} else {
			for (prev = top_affected_room; prev->aff_next;
			     prev = prev->aff_next) {
				if (prev->aff_next == room) {
					prev->aff_next = room->aff_next;
					break;
				}
			}
			if (prev == NULL) {
				bug("Affect_remove_room: cannot find room.", 0);
				return;
			}
		}
		room->aff_next = NULL;

	}

	aff_free(paf);

	affect_check_room(room, where, vector);
	return;
}

/*
 * Strip all affects of a given sn.
 */
void affect_strip_room(ROOM_INDEX_DATA * room, int sn)
{
	AFFECT_DATA *paf;
	AFFECT_DATA *paf_next;

	for (paf = room->affected; paf != NULL; paf = paf_next) {
		paf_next = paf->next;
		if (paf->type == sn)
			affect_remove_room(room, paf);
	}

	return;
}



/*
 * Return true if a room is affected by a spell.
 */
bool is_affected_room(ROOM_INDEX_DATA * room, int sn)
{
	AFFECT_DATA *paf;

	for (paf = room->affected; paf != NULL; paf = paf->next) {
		if (paf->type == sn)
			return TRUE;
	}

	return FALSE;
}



/*
 * Add or enhance an affect.
 */
void affect_join_room(ROOM_INDEX_DATA * room, AFFECT_DATA * paf)
{
	AFFECT_DATA *paf_old;
	bool found;

	found = FALSE;
	for (paf_old = room->affected; paf_old != NULL; paf_old = paf_old->next) {
		if (paf_old->type == paf->type) {
			paf->level = (paf->level += paf_old->level) / 2;
			paf->duration += paf_old->duration;
			paf->modifier += paf_old->modifier;
			affect_remove_room(room, paf_old);
			break;
		}
	}

	affect_to_room(room, paf);
	return;
}


bool is_safe_rspell_nom(int level, CHAR_DATA * victim)
{
	/* ghosts are safe */
	if (!IS_NPC(victim) && IS_SET(victim->state_flags, STATE_GHOST))
		return TRUE;

	/* link dead players who do not have rushing adrenaline are safe */
	if (!IS_NPC(victim) && !IS_PUMPED(victim) && victim->desc == NULL)
		return TRUE;

	if (victim->level < 5 && !IS_NPC(victim))
		return TRUE;

	if (!IS_NPC(victim)
	    && ((level >= victim->level + 5) || (victim->level >= level + 5)))
		return TRUE;

	return FALSE;
}


bool is_safe_rspell(int level, CHAR_DATA * victim)
{
	if (is_safe_rspell_nom(level, victim)) {
		act("The gods protect you.", victim, NULL, NULL, TO_CHAR);
		act("The gods protect $n from the spell of room.", victim, NULL,
		    NULL, TO_ROOM);
		return TRUE;
	} else
		return FALSE;
}


void raffect_to_char(ROOM_INDEX_DATA * room, CHAR_DATA * ch)
{
	AFFECT_DATA *paf;
	if (IS_ROOM_AFFECTED(room, RAFF_RANGER_TRAP)) {
		CHAR_DATA *trapper;	/* character who laid the trap */

		if ((paf = affect_find(room->affected, gsn_rnet_trap)) != NULL) {
		} else if ((paf = affect_find(room->affected, gsn_rblind_trap))
			   != NULL) {
		} else if ((paf = affect_find(room->affected, gsn_ranger_trap))
			   != NULL) {
		} else {
			bug("bad paf for ranger trap", 0);
			return;
		}

		if (number_percent() < (get_skill(ch, gsn_detect_trap)
					+ LEVEL(ch) - paf->level)) {
			char_puts
			    ("You detect a carefully camouflaged trap on the ground.\n",
			     ch);
			check_improve(ch, gsn_detect_trap, TRUE, 1);
			return;	/*trap detectors bypass the trap */
		}

		trapper = get_char_world_unrestricted(paf->player->name);

		/*find trapper according to the affect's player */
		if (trapper == ch)	/*can't trap yourself! */
			return;

		if (paf->type == gsn_rnet_trap) {
			AFFECT_DATA af;
			if (!is_safe_rspell(paf->level, ch)
			    && !IS_AFFECTED(ch, AFF_DETECT_WEB)
			    && !is_affected(ch, gsn_rnet_trap)) {
				char_puts
				    ("You trip on a vine on the ground and are"
				     " pulled up into the air!\n", ch);
				act("$n is suddenly hoisted up in the air and left" " hanging upside down!", ch, NULL, NULL, TO_ROOM);
				af.where = TO_AFFECTS;
				af.type = gsn_rnet_trap;
				af.level = paf->level;
				af.duration = (1 + (paf->level / 30));
				af.location = APPLY_DEX;
				af.modifier = -5;
				af.bitvector = 0;
				affect_join(ch, &af);

				/*force them to sit, limiting most actions */
				ch->position = POS_SITTING;

				/*don't want core dumps for nonexistant trappers */
				if (trapper != NULL) {
					/*tell trapper someone has been ensnared */
					char_printf(trapper,
						    "{W%s{x has been ensnared at {W%s{x.\n",
						    ch->name,
						    mlstr_cval(room->name, ch));
					check_improve(trapper, gsn_rnet_trap,
						      TRUE, 1);
				}
				if (get_skill(ch, gsn_detect_trap) > 0) {
					check_improve(ch, gsn_detect_trap,
						      FALSE, 2);
				}
				do_visible(ch, str_empty);
				affect_remove_room(room, paf);
			}
		}

		/*repeat for other trap types */
		else if (paf->type == gsn_rblind_trap) {
			AFFECT_DATA af;
			if (!is_safe_rspell(paf->level, ch)
			    && !IS_AFFECTED(ch, AFF_BLIND)
			    && !is_affected(ch, sn_lookup("fire breath"))
			    && !is_affected(ch, gsn_rblind_trap)) {
				char_puts
				    ("The room is filled with a flash light"
				     " and you are blinded!\n", ch);
				act("$n is blinded by a sudden flash of light!",
				    ch, NULL, NULL, TO_ROOM);
				af.where = TO_AFFECTS;
				af.type = gsn_rblind_trap;
				af.level = paf->level;
				af.duration = (2 + (paf->level / 20));
				af.location = APPLY_NONE;
				af.modifier = 0;
				af.bitvector = AFF_BLIND;
				affect_join(ch, &af);

				if (trapper != NULL) {
					char_printf(trapper,
						    "{W%s{x has been blinded"
						    " at {W%s{x.\n", ch->name,
						    mlstr_cval(room->name, ch));
					check_improve(trapper, gsn_rblind_trap,
						      TRUE, 1);
				}
				if (get_skill(ch, gsn_detect_trap) > 0) {
					check_improve(ch, gsn_detect_trap,
						      FALSE, 2);
				}

				do_visible(ch, str_empty);
				affect_remove_room(room, paf);
			}
		} else {
			if (!is_safe_rspell(paf->level, ch)
			    && !is_affected(ch, gsn_ranger_trap)) {

				char_puts
				    ("The ground gives way beneath your feet,"
				     " and you impale yourself on a hidden spear!\n",
				     ch);
				act("$n falls through the ground and impales $mself" " on a spear!", ch, NULL, NULL, TO_ROOM);

				damage(ch, ch, dice(paf->level, 6) + 12,
				       TYPE_HUNGER, DAM_RANGER_TRAP, TRUE);

				if (trapper != NULL) {
					char_printf(trapper,
						    "\n{W%s{x has fallen into"
						    " your trap at {W%s{x.\n",
						    ch->name,
						    mlstr_cval(room->name, ch));
					check_improve(trapper, gsn_ranger_trap,
						      TRUE, 1);
				}
				if (get_skill(ch, gsn_detect_trap) > 0) {
					check_improve(ch, gsn_detect_trap,
						      FALSE, 2);
				}
				do_visible(ch, str_empty);
				affect_remove_room(room, paf);
			}
		}
	}
	if (IS_ROOM_AFFECTED(room, RAFF_LSHIELD)) {
		int sn;
		CHAR_DATA *vch;

		if ((sn = sn_lookup("lightning shield")) == -1) {
			bug("Bad sn for lightning shield", 0);
			return;
		}

		for (vch = room->people; vch; vch = vch->next_in_room) {
			if (is_room_owner(vch, room))
				break;
		}

		if (!vch) {
			bug("Owner of lightning shield left the room.", 0);
			free_string(room->owner);
			room->owner = str_dup(str_empty);
			affect_strip_room(room, sn);
		} else {
			char_puts("The protective shield of room blocks you.\n",
				  ch);
			act("$N has entered the room.", vch, NULL, ch, TO_CHAR);
			do_wake(vch, str_empty);

			if ((paf = affect_find(room->affected, sn)) == NULL) {
				bug("Bad paf for lightning shield", 0);
				return;
			}

			if (!is_safe_rspell(paf->level, ch)) {
				damage(vch, ch, dice(paf->level, 4) + 12, sn,
				       DAM_LIGHTNING, TRUE);
				free_string(room->owner);
				room->owner = str_dup(str_empty);
				affect_remove_room(room, paf);
			}
		}
	}

	if (IS_ROOM_AFFECTED(room, RAFF_SHOCKING)) {
		int sn;

		if ((sn = sn_lookup("shocking trap")) == -1) {
			bug("Bad sn for shocking shield", 0);
			return;
		}

		char_puts("The shocking waves of room shocks you.\n", ch);

		if ((paf = affect_find(room->affected, sn)) == NULL) {
			bug("Bad paf for shocking shield", 0);
			return;
		}

		if (!is_safe_rspell(paf->level, ch)) {
			if (check_immune(ch, DAM_LIGHTNING) != IS_IMMUNE)
				damage(ch, ch, dice(paf->level, 4) + 12,
				       TYPE_HUNGER, DAM_TRAP_ROOM, TRUE);
			affect_remove_room(room, paf);
		}
	}

	if (IS_ROOM_AFFECTED(room, RAFF_THIEF_TRAP)) {
		char_puts("The trap, set by someone, blocks you.\n", ch);

		if ((paf = affect_find(room->affected, gsn_settraps)) == NULL) {
			bug("Bad paf for settraps", 0);
			return;
		}

		if (!is_safe_rspell(paf->level, ch)) {
			if (check_immune(ch, DAM_PIERCE) != IS_IMMUNE)
				damage(ch, ch, dice(paf->level, 5) + 12,
				       TYPE_HUNGER, DAM_TRAP_ROOM, TRUE);
			affect_remove_room(room, paf);
		}
	}

	if (IS_ROOM_AFFECTED(room, RAFF_MIDNIGHT)) {
		char_puts("The dark shroud of night cloaks this room.\n", ch);
	}

	if (IS_ROOM_AFFECTED(room, RAFF_SLOW)
	    || IS_ROOM_AFFECTED(room, RAFF_SLEEP))
		char_puts("There is some mist flowing in the air.\n", ch);
}

void raffect_back_char(ROOM_INDEX_DATA * room, CHAR_DATA * ch)
{
	if (IS_ROOM_AFFECTED(room, RAFF_LSHIELD)) {
		int sn;

		if ((sn = sn_lookup("lightning shield")) == -1) {
			bug("Bad sn for lightning shield", 0);
			return;
		}
		if (is_room_owner(ch, room)) {
			free_string(room->owner);
			room->owner = str_dup(str_empty);
			affect_strip_room(room, sn);
		}
	}
}


void do_raffects(CHAR_DATA * ch, const char *argument)
{
	AFFECT_DATA *paf, *paf_last = NULL;

	if (ch->in_room->affected == NULL) {
		char_puts("The room is not affected by any spells.\n", ch);
		return;
	}

	char_puts("The room is affected by the following spells:\n", ch);
	for (paf = ch->in_room->affected; paf != NULL; paf = paf->next) {
		if (paf_last != NULL && paf->type == paf_last->type)
			if (ch->level >= 20)
				char_puts("                      ", ch);
			else
				continue;
		else
			char_printf(ch, "Spell: {c%-15s{x",
				    skill_name(paf->type));

		if (ch->level >= 20) {
			char_printf(ch, ": modifies {c%s{x by {c%d{x ",
				    flag_string(rapply_flags, paf->location),
				    paf->modifier);
			if (paf->duration == -1 || paf->duration == -2)
				char_puts("permanently.", ch);
			else
				char_printf(ch, "for {c%d{x hours.",
					    paf->duration);
		}
		char_puts("\n", ch);
		paf_last = paf;
	}
}
